package com.ctd.utils.jes;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.*;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.crypto.digest.SM3;
import cn.hutool.crypto.symmetric.SM4;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.signers.PlainDSAEncoding;
import org.bouncycastle.crypto.signers.StandardDSAEncoding;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.KeyAgreement;
import java.io.*;
import java.math.BigInteger;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;

/**
 * 用途：
 * 与合作伙伴 交换报文的 简洁版加解密工具
 * <p>
 * 使用方法：
 * 用公钥加密报文，用私钥解密报文
 * <p>
 * 算法说明：
 * 国密算法组合 SM2,SM3,SM4对报文加解密
 * 1. 对任意原文进行SM4加密
 * 2. 对称密钥通过 临时私钥与对方公钥的ECDH算法推导 Key
 * 3. 对原文进行SM3签名
 *
 * @author hzhlu 2023-7-13
 */
public class EMsgHelperV2 {
    public static final Logger log = LoggerFactory.getLogger(EMsgHelperV2.class);
    static Provider BC = new BouncyCastleProvider();
    KeyFactory keyFactory = KeyFactory.getInstance("EC", BC);
    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
    ECNamedCurveParameterSpec sm2Spec = org.bouncycastle.jce.ECNamedCurveTable.getParameterSpec("sm2p256v1");
    private static org.bouncycastle.jce.spec.ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());

    public final static byte[] USER_ID = "1234567812345678".getBytes(StandardCharsets.UTF_8);

    /**
     * 消息标识
     * 可用于与消息发送者的私钥进行关联，便于消息接收者使用发送者的公钥进行报文解密
     * 消息标识会参与到消息加密处理过程中，如果不填此值，缺省为 DEFAULT_MSG_ID=“JES”
     */
    private String msgId;
    private static final String DEFAULT_MSG_ID = "JES";

    /**
     * jwe 中的编码格式 ，true 使用UrlSafe。 缺省false
     */
    Boolean isUrlSafe = true;

    /**
     * KDF推导密钥时的添加因子
     */
    private byte[] kdfSaltBytes;

    /**
     * jwe 接收方的公钥，用于
     * 1. 密钥协商
     * 2. 推导cek
     * 3. 加密
     */
    private PublicKey receiverPubKey;
    private PrivateKey receiverPrivKey;

    /**
     * 临时密钥对的公钥XY值
     */
    byte[] epkPubKeyBytes;

    /**
     * 0 enc msg
     * 1 decrypt msg
     */
    private static final int MODE_ENCRYPT_MSG = 0;
    private static final int MODE_DECRYPT_MSG = 1;
    private int mode;


    /**
     * 发送消息时用于，加密，签名
     * 接收消息时用于 解密，验签
     * <p>
     * jwe 本地接收方的私钥，用于
     * 1. 密钥协商
     * 2. 推导cek
     * 3. 解密
     */
    private PrivateKey senderPrivKey;
    private PublicKey senderPubKey;


    /**
     * 设置公钥
     *
     * @param receiverPubKey
     * @return
     */
    public EMsgHelperV2 setReceiverPubKey(PublicKey receiverPubKey) {
        this.receiverPubKey = receiverPubKey;
        return this;
    }

    public EMsgHelperV2 setReceiverPrivKey(PrivateKey receiverPrivKey) {
        this.receiverPrivKey = receiverPrivKey;
        return this;
    }

    public EMsgHelperV2 setSenderPubKey(PublicKey senderPubKey) {
        this.senderPubKey = senderPubKey;
        return this;
    }

    public EMsgHelperV2 setSenderPrivKey(PrivateKey senderPrivKey) {
        this.senderPrivKey = senderPrivKey;
        return this;
    }

    /**
     * 私钥(D)：00deb3f961a5b121daaac8a2730b2830348d7f124fc211812eda27c03dc7cbc1ae
     * 公钥(Q): 04942e1fa29a58b7e10fd8eac84c1b7d1942663d7e5bc5219ffaba642119ab78e2a7df47bfeafa38a9e5ca789a773f438e79bdbe9b0d371c49caba0d463e7ba209
     *
     * @param receiverPubKeyHex 04942e1fa29a58b7e10fd8eac84c1b7d1942663d7e5bc5219ffaba642119ab78e2a7df47bfeafa38a9e5ca789a773f438e79bdbe9b0d371c49caba0d463e7ba209
     * @return
     */
    public EMsgHelperV2 setReceiverPubKey(String receiverPubKeyHex) throws InvalidKeySpecException {
        ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(sm2Spec.getCurve().decodePoint(Hex.decode(receiverPubKeyHex)), sm2Spec);
        this.receiverPubKey = keyFactory.generatePublic(publicKeySpec);
        return this;
    }

    public EMsgHelperV2 setReceiverPrivKey(String receiverPrivKeyHex) throws InvalidKeySpecException {
        ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(new java.math.BigInteger(1, Hex.decode(receiverPrivKeyHex)), sm2Spec);
        this.receiverPrivKey = keyFactory.generatePrivate(privateKeySpec);
        return this;
    }


    /**
     * 私钥(D)：00deb3f961a5b121daaac8a2730b2830348d7f124fc211812eda27c03dc7cbc1ae
     * 公钥(Q): 04942e1fa29a58b7e10fd8eac84c1b7d1942663d7e5bc5219ffaba642119ab78e2a7df47bfeafa38a9e5ca789a773f438e79bdbe9b0d371c49caba0d463e7ba209
     *
     * @param privKeyHex 00deb3f961a5b121daaac8a2730b2830348d7f124fc211812eda27c03dc7cbc1ae
     * @return
     * @throws InvalidKeySpecException
     */
    public EMsgHelperV2 setSenderPrivKey(String privKeyHex) throws InvalidKeySpecException {
        ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(new java.math.BigInteger(1, Hex.decode(privKeyHex)), sm2Spec);
        this.senderPrivKey = keyFactory.generatePrivate(privateKeySpec);
        return this;
    }

    public EMsgHelperV2 setSenderPubKey(String senderPubKeyHex) throws InvalidKeySpecException {
        ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(sm2Spec.getCurve().decodePoint(Hex.decode(senderPubKeyHex)), sm2Spec);
        this.senderPubKey = keyFactory.generatePublic(publicKeySpec);
        return this;
    }

    public String getMsgId() {
        return msgId;
    }

    /**
     * 设置盐
     *
     * @param msgId
     * @return
     */
    private void initSaltByMsgId(String msgId) {
        if (msgId.length() > 40) {
            throw new RuntimeException("msgId length must less than 40 char.");
        }
        if (msgId.contains(".")) {
            throw new RuntimeException("msgId cannot contain the character [.]");
        }
        if (StrUtil.isEmpty(msgId)) {
            throw new RuntimeException("msgId cannot be empty.");
        } else {
            this.kdfSaltBytes = new SM3().digest(msgId);
        }
    }

    /**
     * 构造空对象
     */
    public EMsgHelperV2() throws NoSuchAlgorithmException {
        this.msgId = DEFAULT_MSG_ID;
        initSaltByMsgId(msgId);
    }

    public EMsgHelperV2(String msgId) throws NoSuchAlgorithmException {
        this.msgId = msgId;
        initSaltByMsgId(msgId);
    }

//
//    /**
//     * 构造Jwe加密环境
//     *
//     * @param pubKey jwe加密报文使用的接收方公钥
//     */
//    public EMsgHelperV2(PublicKey pubKey) throws NoSuchAlgorithmException {
//        this(null, pubKey);
//    }
//
//    public EMsgHelperV2(String msgId, PublicKey pubKey) throws NoSuchAlgorithmException {
//        this.msgId = msgId;
//        this.pubKey = pubKey;
//        initSaltByMsgId(msgId);
//    }

//
//    /**
//     * 构造Jwe解密环境
//     *
//     * @param privKey 解密jwe使用的接收者私钥
//     */
//    public EMsgHelperV2(PrivateKey privKey) throws NoSuchAlgorithmException {
//        this(null, privKey);
//    }
//
//    public EMsgHelperV2(String msgId, PrivateKey privKey) throws NoSuchAlgorithmException {
//        this.msgId = msgId;
//        this.privKey = privKey;
//        initSaltByMsgId(msgId);
//    }


    /**
     * 解析 SM2密钥对，返回对应的Hex格式
     *
     * @param keyPair
     * @return
     */
    public Map<String, String> parseKeyPair(KeyPair keyPair) {
        String privKeyHex = Hex.toHexString(BCUtil.encodeECPrivateKey(keyPair.getPrivate()));
        String pubKeyHex = Hex.toHexString(((BCECPublicKey) keyPair.getPublic()).getQ().getEncoded(false));
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("pub", pubKeyHex);
        resultMap.put("priv", privKeyHex);
        return resultMap;
    }

    /**
     * CTD专用的JWE 加密方法
     * 对payload加密，生成JWE加密对象
     *
     * @param payload 待加密的明文字节数组
     * @return 压缩格式的jwe，用 "." 分隔的5个部分: protected.cek.iv.cipherTxt.tag
     * BASE64URL(UTF8(JWE Protected Header)) || ’.’ ||
     * BASE64URL(JWE Encrypted Key) || ’.’ ||
     * BASE64URL(JWE Initialization Vector) || ’.’ ||
     * BASE64URL(JWE Ciphertext) || ’.’ ||
     * BASE64URL(JWE Authentication Tag)
     * @author hzhlu
     * @date 2022-1-14
     */
    public String encrypt(byte[] payload) throws NoSuchAlgorithmException, InvalidKeyException {
        this.mode = MODE_ENCRYPT_MSG;

        // step1. 秘钥协商
        byte[] z = generateEcdhSecret(receiverPubKey, genEpkPrivKey4SM2());
        log.trace("epkPubKey[{}]: {}", epkPubKeyBytes.length, Hex.toHexString(epkPubKeyBytes));

        // step2. 协商导出的agreedKey = cek
        byte[] cek = kdf(z, epkPubKeyBytes.length);

        // step3. 生成随机的iv
        byte[] iv = randBytes(16);

        // step4. 生成 payload的密文 cipherTxt
        byte[] cipher = encryptContent4SM4(payload, cek, iv);

        // step5. 计算 tag
        byte[] tag = calculateTag(msgId.getBytes(StandardCharsets.UTF_8), epkPubKeyBytes, iv, cipher, cek);

        // step6. 计算 sign ,2023-8-16 增加签名
        byte[] sign = signature(tag);

        // 返回压缩格式的jwe
        String jweCompact = msgId + "." + serialize(isUrlSafe, epkPubKeyBytes, iv, cipher, tag, sign);
        log.trace("result[{}]: {}", jweCompact.length(), jweCompact);
        return jweCompact;
    }

    private byte[] signature(byte[] srcData) {
        SM2 sm2 = new SM2(senderPrivKey, null);
        sm2.setEncoding(new PlainDSAEncoding());  // 生成压缩格式的签名结果
        return sm2.sign(srcData, USER_ID);
    }


    /**
     * 生成国密的临时密钥对
     *
     * @return
     */
    private PrivateKey genEpkPrivKey4SM2() {
        KeyPair ephemeralKeyPair = SecureUtil.generateKeyPair("SM2"); // 创建临时密钥对
        byte[] tempPubKeyCode = ((ECPublicKey) ephemeralKeyPair.getPublic()).getQ().getEncoded(false);
        // 去掉Q值中的首字节04，只保留公钥的XY值
        epkPubKeyBytes = subArray(tempPubKeyCode, 1, tempPubKeyCode.length - 1);
        return ephemeralKeyPair.getPrivate();
    }


    /**
     * 解密
     *
     * @param encTxt 由”.“分隔的加密字符串
     * @return
     */
    public byte[] decrypt(String encTxt) throws NoSuchAlgorithmException, InvalidKeyException {
        this.mode = MODE_DECRYPT_MSG;
        String[] encTxtItems = deserialize(encTxt);
        // [1] msgId
        if (!msgId.equalsIgnoreCase(encTxtItems[0])) {
            throw new ValidateException("not support [" + encTxtItems[0] + "] msg version");
        }

        // [2] epk SM2 临时公钥的Q值
        epkPubKeyBytes = Base64.decode(encTxtItems[1]);
        log.trace("epk-PubK :{}", Hex.toHexString(epkPubKeyBytes));
        String pubX = Hex.toHexString(subArray(epkPubKeyBytes, 0, 32));
        String pubY = Hex.toHexString(subArray(epkPubKeyBytes, 32, 32));
        PublicKey epkPubKey = getPublickeyFromXY(new BigInteger(pubX, 16), new BigInteger(pubY, 16));

        // 协商密钥
        PrivateKey privKey = (mode == MODE_ENCRYPT_MSG) ? senderPrivKey : receiverPrivKey;
        byte[] z = generateEcdhSecret(epkPubKey, privKey);

        // step2. 提取并校验CEK
        byte[] cek = kdf(z, epkPubKeyBytes.length);

        // 解密密文
        byte[] iv = Base64.decode(encTxtItems[2]);
        byte[] cipherTxt = Base64.decode(encTxtItems[3]);
        byte[] tag = Base64.decode(encTxtItems[4]);
        byte[] msgSign = Base64.decode(encTxtItems[5]);
        byte[] planeBytes = decryptCipher(epkPubKeyBytes, cipherTxt, iv, tag, cek, msgSign);
        return planeBytes;
    }

    private PublicKey getPublickeyFromXY(BigInteger x, BigInteger y) {
        ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecParameterSpec);
        return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
    }

    private byte[] decryptCipher(byte[] aadBytes, byte[] cipher, byte[] iv, byte[] msgTag, byte[] cek, byte[] msgSign) {
        // 校验tag
        byte[] chkTag = calculateTag(msgId.getBytes(StandardCharsets.UTF_8), aadBytes, iv, cipher, cek);
        if (!secureEquals(msgTag, chkTag)) {
            log.debug("orign msgTag [{}] , expect tag [{}]", Hex.toHexString(msgTag), Hex.toHexString(chkTag));
            throw new ValidateException("invalidate msg tag");
        }

        // step6. 计算 sign ,2023-8-16 增加签名
        SM2 sm2 = new SM2(null, senderPubKey);
        if (msgSign.length == 64) {
            sm2.setEncoding(new PlainDSAEncoding());  // 压缩格式的签名编码
        } else {
            sm2.setEncoding(new StandardDSAEncoding());  // tlv的签名编码
        }
        boolean isValidate = sm2.verify(chkTag, msgSign, USER_ID);
        if (!isValidate) {
            log.debug("orign msgTag [{}] , expect tag [{}]", Hex.toHexString(msgTag), Hex.toHexString(chkTag));
            throw new ValidateException("invalidate msg sign");
        }

        // 解密
        return decryptContent4SM4(cipher, cek, iv);
    }


    /**
     * HMAC 计算授权码
     *
     * @param ver       固定值 V1
     * @param epkPubKey SM2临时密钥对的公钥Q值
     * @param iv        向量
     * @param cipher    密文
     * @param cek       密钥，HMac-Key 使用前 16 字节
     * @return
     */
    private byte[] calculateTag(byte[] ver, byte[] epkPubKey, byte[] iv, byte[] cipher, byte[] cek) {
        byte[] alBytes = ByteUtil.longToBytes(epkPubKey.length, ByteOrder.BIG_ENDIAN); // epkPubKey length Big-Ending order
        byte[] concat = concat(ver, epkPubKey, iv, cipher, cek, alBytes);
        HMac mac = new HMac(HmacAlgorithm.HmacSM3, cek);
        byte[] macBytes = mac.digest(concat);
        byte[] tag = leftHalf(macBytes);
        if (log.isTraceEnabled()) {
            log.trace("1.ver[{}]: {}", ver.length, Hex.toHexString(ver));
            log.trace("2.epkPubKey[{}]: {}", epkPubKey.length, Hex.toHexString(epkPubKey));
            log.trace("3.iv[{}]: {}", iv.length, Hex.toHexString(iv));
            log.trace("4.cipher[{}]: {}", cipher.length, Hex.toHexString(cipher));
            log.trace("5.HMacKey[{}]: {}", cek.length, Hex.toHexString(cek));
            log.trace("6.al[{}]: {}", alBytes.length, Hex.toHexString(alBytes));
            log.trace("concat-bytes[{}]: {}", concat.length, Hex.toHexString(concat));
            log.trace("mac[{}]: {}", macBytes.length, Hex.toHexString(macBytes));
            log.trace("tag[{}]: {}", tag.length, Hex.toHexString(tag));
        }
        return tag;
    }


    /**
     * 加密
     *
     * @param plainTxt
     * @param cek
     * @param iv
     * @return
     */
    private byte[] encryptContent4SM4(byte[] plainTxt, byte[] cek, byte[] iv) {
        SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, cek, iv);
        byte[] cipher = sm4.encrypt(plainTxt);
        if (log.isTraceEnabled()) {
            log.trace("cek[{}]: {} ", cek.length, Hex.toHexString(cek));
            log.trace("src[{}]: {}", plainTxt.length, Hex.toHexString(plainTxt));
            log.trace("iv[{}]: {}", iv.length, Hex.toHexString(iv));
            log.trace("cipher[{}]: {}", cipher.length, Hex.toHexString(cipher));
        }
        return cipher;
    }


    private byte[] decryptContent4SM4(byte[] cipherTxt, byte[] cek, byte[] iv) {
        SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, cek, iv);
        byte[] plainTxt = sm4.decrypt(cipherTxt);
        if (log.isTraceEnabled()) {
            log.trace("cek[{}]: {}", cek.length, Hex.toHexString(cek));
            log.trace("cipherTxt[{}]: {}", cipherTxt.length, Hex.toHexString(cipherTxt));
            log.trace("iv[{}]: {}", iv.length, Hex.toHexString(iv));
            log.trace("plainTxt[{}]: {}", plainTxt.length, Hex.toHexString(plainTxt));
        }
        return plainTxt;
    }

    /**
     * 秘钥导出
     *
     * @param z  本地私钥和接收方公钥协商的对称密钥因子
     * @param al protected字节数
     * @return cek 内容加密的sm4key
     */
    private byte[] kdf(byte[] z, int al) {
        byte[] alBytes = ByteUtil.longToBytes(al, ByteOrder.BIG_ENDIAN); // aad length Big-Ending order
        byte[] concat = concat(z, kdfSaltBytes, alBytes);
        log.trace("al[{}]: {}  al-value: {}", alBytes.length, Hex.toHexString(alBytes), al);
        log.trace("kdfSalt[{}]: {}", kdfSaltBytes.length, Hex.toHexString(kdfSaltBytes));
        log.trace("concat[{}]: {}", concat.length, Hex.toHexString(concat));
        byte[] agreedKey = leftHalf(SmUtil.sm3().digest(concat));
        return agreedKey;
    }

    /**
     * ECDH 秘钥协商
     *
     * @param peerPubKey 对方公钥
     * @param privateKey 本方私钥
     * @return
     */
    private byte[] generateEcdhSecret(PublicKey peerPubKey, PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeyException {
        KeyAgreement kaTemp = KeyAgreement.getInstance("ECDH", BC);
        kaTemp.init(privateKey);
        kaTemp.doPhase(peerPubKey, true);
        byte[] z = kaTemp.generateSecret();
        log.trace("z[{}]: {}", z.length, Hex.toHexString(z));
        return z;
    }


    // ----------- 工具函数 ------------
    private static final String PERIOD_SEPARATOR = ".";
    private static final String PERIOD_SEPARATOR_REGEX = "\\.";
    private static final String EMPTY_STRING = "";
    public static final byte[] EMPTY_BYTES = new byte[0];

    public static boolean secureEquals(byte[] bytes1, byte[] bytes2) {
        bytes1 = (bytes1 == null) ? EMPTY_BYTES : bytes1;
        bytes2 = (bytes2 == null) ? EMPTY_BYTES : bytes2;

        int shortest = Math.min(bytes1.length, bytes2.length);
        int longest = Math.max(bytes1.length, bytes2.length);

        int result = 0;

        // should be a time-constant comparison with respect to the length
        for (int i = 0; i < shortest; i++) {
            result |= bytes1[i] ^ bytes2[i];
        }

        return (result == 0) && (shortest == longest);
    }

    public static byte[] concat(byte[]... byteArrays) {
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            for (byte[] bytes : byteArrays) {
                byteArrayOutputStream.write(bytes);
            }
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            throw new IllegalStateException("IOEx from ByteArrayOutputStream?!", e);
        }
    }

    public static byte[] subArray(byte[] inputBytes, int startPos, int length) {
        byte[] subArray = new byte[length];
        System.arraycopy(inputBytes, startPos, subArray, 0, subArray.length);
        return subArray;
    }

    public static byte[] leftHalf(byte[] inputBytes) {
        return subArray(inputBytes, 0, (inputBytes.length / 2));
    }

    public static String[] deserialize(String compactSerialization) {
        String[] parts = compactSerialization.split(PERIOD_SEPARATOR_REGEX);

        if (compactSerialization.endsWith(PERIOD_SEPARATOR)) {
            String[] tempParts = new String[parts.length + 1];
            System.arraycopy(parts, 0, tempParts, 0, parts.length);
            tempParts[parts.length] = EMPTY_STRING;
            parts = tempParts;
        }

        return parts;
    }

    public static String serialize(String... parts) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < parts.length; i++) {
            String part = (parts[i] == null) ? EMPTY_STRING : parts[i];
            sb.append(part);
            if (i != parts.length - 1) {
                sb.append(PERIOD_SEPARATOR);
            }
        }
        return sb.toString();
    }

    public static String serialize(boolean isUrlSafe, byte[]... parts) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < parts.length; i++) {
            String part = (parts[i] == null) ? EMPTY_STRING : (isUrlSafe ? Base64.encodeUrlSafe(parts[i]) : Base64.encode(parts[i]));
            sb.append(part);
            if (i != parts.length - 1) {
                sb.append(PERIOD_SEPARATOR);
            }
        }
        return sb.toString();
    }

    public static byte[] randBytes(int length) {
        byte[] buffer = new byte[length];
        new SecureRandom().nextBytes(buffer);
        return buffer;
    }

    public static KeyPair generateKeyPair() {
        return SecureUtil.generateKeyPair("SM2");
    }


    /**
     * 读取pem文件（私钥），获取秘钥对
     *
     * @param pemFile
     * @return
     * @throws Exception
     */
    public static KeyPair readKeyPair(String pemFile) throws IOException {
        InputStream res = new FileInputStream(pemFile);
        return readKeyPair(res);
    }

    /**
     * 读取pem文件（私钥），获取秘钥对
     *
     * @param res
     * @return
     * @throws Exception
     */
    public static KeyPair readKeyPair(InputStream res) throws IOException {
        Reader fRd = new BufferedReader(new InputStreamReader(res));
        Security.addProvider(new BouncyCastleProvider());
        PEMParser pemRd = new PEMParser(fRd);
        PEMKeyPair pemPair;
        KeyPair pair;
        ASN1ObjectIdentifier ecOID = (ASN1ObjectIdentifier) pemRd.readObject();
        X9ECParameters ecSpec = ECNamedCurveTable.getByOID(ecOID);
        if (ecSpec == null) {
            throw new IllegalArgumentException("ecSpec not found for named curve");
        }
        pemPair = (PEMKeyPair) pemRd.readObject();
        pair = new JcaPEMKeyConverter().setProvider("BC").getKeyPair(pemPair);
        return pair;
    }


}
